#include "Main.h"


VOID ErrorReporter(JSContext *jsCx, const CHAR *szMessage, JSErrorReport *jsReport)
{
	if(!jsReport || !szMessage || !jsCx)
		return;

	ScriptData* js = (ScriptData*)JS_GetContextPrivate(jsCx);

	if(!js)
		return;

	if(JSREPORT_IS_WARNING(jsReport->flags))
		Log("[Warning] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);
	else Log("[Error] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);

	if(js && !js->IsInGameScript())
	{
		CHAR szBuffer[8192] = "";
		if(JSREPORT_IS_WARNING(jsReport->flags))
			sprintf(szBuffer, "[Warning] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);
		else sprintf(szBuffer, "[Error] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);
		MessageBox(NULL, szBuffer, "OOG Error!", NULL);
	}

	if(strlen(szMessage) <= 200)
	{
		if(JSREPORT_IS_WARNING(jsReport->flags))
			PrintText("[Warning] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);
		else PrintText("[Error] %s (line: %d): %s", jsReport->filename, jsReport->lineno, szMessage);
	}
	else {
		if(JSREPORT_IS_WARNING(jsReport->flags))
			PrintText("w3jsp: Logged Warning to w3jsp.log");
		else PrintText("w3jsp: Logged Error to w3jsp.log");
	}

//	if(Vars.bQuitOnError && D2CLIENT_GetPlayerUnit() && !JSREPORT_IS_WARNING(jsReport->flags))
//		D2CLIENT_ExitGame();
}

BOOL CheckAbortCallback(JSContext *jsCx, JSScript *jsScript)
{
	ScriptData* js = (ScriptData*)JS_GetContextPrivate(jsCx);

	if(js)
		if(js->isStopped())
			return JS_FALSE;

	if(js->IsInGameScript())
		if(!InGame() || Vars.bAbortScripts) 
			return JS_FALSE;

	return JS_TRUE;
}


ScriptData* CreateContext(char* lpszFileName)
{

	JSRuntime* jsRuntime = JS_Init(0x4000000);

	if(!jsRuntime)
		return NULL;

	ScriptData* jscontext = NULL;

	JSContext* tmpcx = JS_NewContext(jsRuntime, STACK_CHUNK_SIZE);
	
	if(!tmpcx) 
	{ 
		Log("Couldn't create JSContext!");
		MessageBox(NULL, "Couldn't create JSContext!", "Critical Error", NULL);
		return NULL; 
	}

	JSObject* tmpobj = JS_NewObject(tmpcx, &my_global_class, 0, 0);

	if(!tmpobj) 
	{ 
		Log("Couldn't create JSObject!");
		MessageBox(NULL, "Couldn't create JSObject!", "Critical Error", NULL);
		JS_DestroyContext(tmpcx);
		return NULL; 
	}

	jscontext = new ScriptData(tmpcx, tmpobj, lpszFileName);

	JS_SetContextPrivate(tmpcx, jscontext);

	jscontext->SetRuntime(jsRuntime);
	
	return jscontext;
}

BOOL ExecuteScriptCommand(CHAR* szScript)
{
	ScriptData* cx = CreateContext("CommandLine");

	if(!cx)
		return FALSE;

	cx->SetInGame(TRUE);

	JS_SetErrorReporter(cx->GetContext(), ErrorReporter);
	JS_SetBranchCallback(cx->GetContext(), CheckAbortCallback);

	if(!JS_InitStandardClasses(cx->GetContext(), cx->GetObject()))
	{
		Log("Couldn't init the standard classes!");

		delete cx;
		return FALSE;
	}

	if(!JS_DefineFunctions(cx->GetContext(), cx->GetObject(), my_functions) || !JS_DefineFunctions(cx->GetContext(), cx->GetObject(), file_functions)) 
	{ 
		Log("Couldn't init the ingame classes!"); 

		delete cx;
		return FALSE; 
	}

	myPlayer* lpPlayer = new myPlayer;

	if(!lpPlayer)
	{
		Log("Couldn't define the 'me' object!");

		delete cx;

		return FALSE;
	}

	memset(lpPlayer, NULL, sizeof(myPlayer));

	if((*p_GAME_GameInfos))
		lpPlayer->dwPlayerSlot = (*p_GAME_GameInfos)->wPlayerSlot;	
	lpPlayer->_dwPrivateType = PRIVATE_PLAYER;

	JSObject *me = JS_DefineObject(cx->GetContext(), cx->GetObject(), "me", &player_class, NULL, JSPROP_ENUMERATE);
	
	if(!me || !JS_DefineProperties(cx->GetContext(), me, me_props) || !JS_DefineFunctions(cx->GetContext(), me, player_methods))
	{
		Log("Couldn't create the 'me' object!");

		if(me)
			delete me;

		delete lpPlayer;
		delete cx;

		return FALSE;
	}	

	JS_InitClass(cx->GetContext(), cx->GetObject(), 0, &player_class, 0, 0, player_props, player_methods, 0, 0);
	JS_SetPrivate(cx->GetContext(), me, lpPlayer);

	jsval rval, rval2;

	if(JS_EvaluateScript(cx->GetContext(), cx->GetObject(), szScript,
		strlen(szScript), "Command Line", NULL, &rval))
	{
		jsval mainval;

		JS_GetProperty(cx->GetContext(), cx->GetObject(), "main", &mainval); 

		if(JSVAL_IS_OBJECT(mainval) && JS_ObjectIsFunction(cx->GetContext(), JSVAL_TO_OBJECT(mainval)))
				JS_CallFunctionValue(cx->GetContext(), cx->GetObject(), mainval, 0, NULL, &rval2);

	}


	return TRUE;
}

BOOL ExecuteScript(CHAR* szPath, BOOL bInGame)
{
	CHAR szFilePath[MAX_PATH] = "";
	
	sprintf(szFilePath,"%s\\%s%c", Vars.szPath, szPath, NULL);

	if(access(szFilePath, NULL))
		return FALSE;

	ScriptData* cx = CreateContext(szPath);

	if(!cx)
		return FALSE;

	cx->SetInGame(bInGame);


	if(!JS_InitStandardClasses(cx->GetContext(), cx->GetObject()))
	{
		Log("Couldn't init the standard classes!");

		delete cx;
		return FALSE;
	}

	if(!JS_DefineFunctions(cx->GetContext(), cx->GetObject(), my_functions) || !JS_DefineFunctions(cx->GetContext(), cx->GetObject(), file_functions)) 
	{ 
		Log("Couldn't init the ingame classes!"); 

		delete cx;
		return FALSE; 
	}
	myPlayer* lpPlayer = new myPlayer;

	if(!lpPlayer)
	{
		Log("Couldn't define the 'me' object!");

		delete cx;

		return FALSE;
	}

	memset(lpPlayer, NULL, sizeof(myPlayer));

	if((*p_GAME_GameInfos))
		lpPlayer->dwPlayerSlot = (*p_GAME_GameInfos)->wPlayerSlot;	
	lpPlayer->_dwPrivateType = PRIVATE_PLAYER;

	JSObject *me = JS_DefineObject(cx->GetContext(), cx->GetObject(), "me", &player_class, NULL, JSPROP_ENUMERATE);
	
	if(!me || !JS_DefineProperties(cx->GetContext(), me, me_props) || !JS_DefineFunctions(cx->GetContext(), me, player_methods))
	{
		Log("Couldn't create the 'me' object!");

		if(me)
			delete me;

		delete lpPlayer;
		delete cx;

		return FALSE;
	}	

	JS_InitClass(cx->GetContext(), cx->GetObject(), 0, &player_class, 0, 0, player_props, player_methods, 0, 0);
	JS_SetPrivate(cx->GetContext(), me, lpPlayer);

	JS_SetErrorReporter(cx->GetContext(), ErrorReporter);
	JS_SetBranchCallback(cx->GetContext(), CheckAbortCallback);

	HANDLE ThreadHandle = CreateThread(NULL, NULL, RunScript, cx, CREATE_SUSPENDED ,NULL);

	cx->SetScriptThreadHandle(ThreadHandle);
	ResumeThread(ThreadHandle);

	return TRUE;
}

DWORD WINAPI RunScript(LPVOID lpParams)
{
	ScriptData* cx = (ScriptData*)lpParams;
	jsval rval, rval2;

	if(!cx)
		return NULL;

	srand((INT)time(NULL));

	CHAR szPath[MAX_PATH] = {NULL};
	sprintf(szPath, "%s%s%c", Vars.szPath,cx->GetFile(), NULL);


	LinkConstants(cx->GetContext(), cx->GetObject());
	JS_SetVersion(cx->GetContext(), JSVERSION_1_7);
	JS_SetOptions(cx->GetContext(), JSOPTION_NATIVE_BRANCH_CALLBACK);
/*
	Log("Loading the file %s", szPath);

	Log("Callin compileFile with 0x%x, 0x%x, %s", cx->GetContext(), cx->GetObject(), szPath);
	JSScript* myScript = JS_CompileFile(cx->GetContext(), cx->GetObject(), szPath);


	if(myScript)
	{
		Log("nInCe");
		JS_ExecuteScript(cx->GetContext(), cx->GetObject(), myScript, &rval);

			jsval mainval;
			JS_GetProperty(cx->GetContext(), cx->GetObject(), "main", &mainval); 
			if(JSVAL_IS_OBJECT(mainval) && JS_ObjectIsFunction(cx->GetContext(), JSVAL_TO_OBJECT(mainval)))
				JS_CallFunctionValue(cx->GetContext(), cx->GetObject(), mainval, 0, NULL, &rval2);

			JS_DestroyScript(cx->GetContext(), myScript);
	}
	else Log("Couldn't compile %s", szPath);
*/

	HANDLE hFile = CreateFileA(szPath, GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if(hFile == INVALID_HANDLE_VALUE)
		return FALSE;


	UINT nBytes = 0;
	UINT nSize = GetFileSize(hFile, NULL);
	PCHAR lpFile = new CHAR[nSize+1];
	memset(lpFile, NULL, nSize+1);
	ReadFile(hFile, lpFile, nSize, (LPDWORD)&nBytes, FALSE);
	CloseHandle(hFile);

	if(JS_EvaluateScript(cx->GetContext(), cx->GetObject(), lpFile,
		nSize, cx->GetFile(), TRUE, &rval))
	{
		jsval mainval;

		JS_GetProperty(cx->GetContext(), cx->GetObject(), "main", &mainval); 

		if(JSVAL_IS_OBJECT(mainval) && JS_ObjectIsFunction(cx->GetContext(), JSVAL_TO_OBJECT(mainval)))
		{
				JS_CallFunctionValue(cx->GetContext(), cx->GetObject(), mainval, 0, NULL, &rval2);
		}
	}
	
	delete cx;	
	delete[] lpFile;

	return NULL;
}

LinkEvents_t LinkEvents[] = {
	{EVENT_CHATCMD, "EVENT_CHATCMD"},
	{EVENT_UNITLIFEHIT, "EVENT_UNITLIFEHIT"},
	{EVENT_RESUPDATE, "EVENT_RESUPDATE"},
	{EVENT_CREATEUNIT, "EVENT_CREATEUNIT"},
	{EVENT_UNITNEWCLASSID, "EVENT_UNITNEWCLASSID"},
};

VOID LinkConstants(JSContext* cx, JSObject* globalObj)
{
	for(INT i = 0; i < ArraySize(LinkEvents); i++)
		JS_DefineProperty(cx, globalObj, LinkEvents[i].szName, INT_TO_JSVAL(LinkEvents[i].dwEventId),NULL, NULL, JSPROP_READONLY | JSPROP_ENUMERATE);
}